<script lang="ts" setup>
import type { MenuApi, RoleApi } from '#/api/types';

import { computed, ref, watch } from 'vue';

import { useVbenDrawer } from '@vben/common-ui';

import { Card, Checkbox, Empty, message, Spin } from 'ant-design-vue';

import { getAllMenuListApi } from '#/api/core/modules/menu';
import { getRoleMenuListApi, updateRoleMenuApi } from '#/api/core/modules/role';
import { $t } from '#/locales';

// 定义事件
const emit = defineEmits<{
  (e: 'success'): void;
}>();

// 加载状态
const initialLoading = ref(true);

// 菜单树数据
const menuTree = ref<MenuApi.MenuTree[]>([]);

// 已选择的菜单ID列表 - 存储字符串类型的ID
const selectedMenuIds = ref<string[]>([]);

// 角色信息
const currentRole = ref<null | RoleApi.RoleVO>(null);

// 存储角色最初的菜单集合，用于对比变更 - 存储字符串类型的ID
const originalMenuIds = ref<string[]>([]);

// 提取并分类菜单
const categorizedMenus = computed(() => {
  // 顶级菜单作为分类
  return menuTree.value.map((topMenu) => {
    // 获取子菜单作为具体项目
    const children = topMenu.children || [];
    return {
      id: topMenu.id,
      name: $t(topMenu.menuName),
      code: topMenu.perms,
      children,
    };
  });
});

// 监听角色变化重新加载菜单数据
watch(
  () => currentRole.value?.id,
  (newRoleId) => {
    if (newRoleId) {
      loadData();
    }
  },
  { immediate: false },
);

// 抽屉组件
const [Drawer, drawerApi] = useVbenDrawer({
  title: $t('system.role.setMenus'),

  // 表单确认处理
  async onConfirm() {
    try {
      if (!currentRole.value?.id) {
        message.error($t('system.role.idRequired'));
        return false;
      }

      // 锁定抽屉
      drawerApi.setState({ loading: true });

      // 角色ID
      const roleId = currentRole.value.id;

      // 转换选中的菜单ID为数字
      const selectedIds = selectedMenuIds.value.map(Number);

      // 计算需要添加和移除的菜单
      const assignMenuIds = selectedIds.filter(
        (id) => !originalMenuIds.value.includes(String(id)),
      );
      const removeMenuIds = originalMenuIds.value
        .map(Number)
        .filter((id) => !selectedIds.includes(id));

      // 更新角色菜单
      if (assignMenuIds.length > 0 || removeMenuIds.length > 0) {
        const updateResult = await updateRoleMenuApi({
          roleId,
          assignMenuIds,
          removeMenuIds,
        });

        if (!updateResult.success) {
          message.warning(
            updateResult.message || $t('system.role.menuUpdateFailed'),
          );
          return false;
        }
      }

      message.success($t('common.operationSuccess'));
      emit('success');

      // 主动关闭抽屉
      drawerApi.close();
      return true;
    } catch {
      return false;
    } finally {
      drawerApi.setState({ loading: false });
    }
  },

  // 打开关闭处理
  onOpenChange(isOpen) {
    if (isOpen) {
      // 获取数据
      const data = drawerApi.getData<RoleApi.RoleVO>();
      if (data && data.id) {
        currentRole.value = data;
        // 加载菜单数据
        loadData();
      }
    } else {
      // 关闭时重置数据
      selectedMenuIds.value = [];
      currentRole.value = null;
      menuTree.value = [];
    }
  },
});

// 加载菜单树数据
async function loadData() {
  initialLoading.value = true;
  try {
    // 如果有角色ID，同时加载所有菜单和角色已分配的菜单
    if (currentRole.value?.id) {
      await Promise.all([loadAllMenus(), loadRoleMenus(currentRole.value.id)]);
    }
  } catch (error) {
    console.error($t('system.role.menuLoadErrorLog'), error);
    message.error($t('common.loadFailed'));
  } finally {
    initialLoading.value = false;
  }
}

// 加载所有菜单树
async function loadAllMenus() {
  try {
    // 获取所有菜单树
    const menuResult = await getAllMenuListApi();

    // 解析菜单树数据
    const menuData = menuResult?.data || menuResult;

    if (Array.isArray(menuData)) {
      menuTree.value = menuData;
    } else {
      console.warn($t('system.menu.dataNotArray'), menuData);
      menuTree.value = [];
    }
  } catch (error) {
    console.error($t('system.menu.loadAllErrorLog'), error);
    message.error($t('common.loadFailed'));
  }
}

// 加载角色已分配的菜单
async function loadRoleMenus(roleId: number) {
  try {
    // 获取角色已分配的菜单
    const roleMenuResult = await getRoleMenuListApi(roleId);
    // 解析角色已分配的菜单ID
    const menuData = roleMenuResult?.data || roleMenuResult;

    if (Array.isArray(menuData)) {
      // 从MenuVO[]中提取菜单ID
      const menuIds = menuData.map((menu) => String(menu.id));
      selectedMenuIds.value = menuIds;
      originalMenuIds.value = [...menuIds];
    } else if (menuData) {
      console.warn($t('system.role.menuDataNotArray'), menuData);
    }
  } catch (error) {
    console.error($t('system.role.menuLoadErrorLog'), error);
    message.error($t('common.loadFailed'));
  }
}

// 处理菜单选择
function handleMenuChange(menuId: string, checked: boolean) {
  if (checked) {
    // 添加到选中列表
    if (!selectedMenuIds.value.includes(menuId)) {
      selectedMenuIds.value.push(menuId);
    }
  } else {
    // 从选中列表移除
    const index = selectedMenuIds.value.indexOf(menuId);
    if (index !== -1) {
      selectedMenuIds.value.splice(index, 1);
    }
  }
}

// 切换分类下所有菜单
function toggleCategoryMenus(
  category: { children: MenuApi.MenuTree[]; id: number },
  checked: boolean,
) {
  if (!category.children || !Array.isArray(category.children)) return;

  // 确保ID为字符串类型
  const menuIds = category.children.map((m) => String(m.id));

  if (checked) {
    // 添加该分类下所有菜单
    menuIds.forEach((id) => {
      if (!selectedMenuIds.value.includes(id)) {
        selectedMenuIds.value.push(id);
      }
    });
  } else {
    // 移除该分类下所有菜单
    selectedMenuIds.value = selectedMenuIds.value.filter(
      (id) => !menuIds.includes(id),
    );
  }
}

// 计算分类的全选状态
function getCategoryCheckStatus(category: { children: MenuApi.MenuTree[] }) {
  if (
    !category.children ||
    !Array.isArray(category.children) ||
    category.children.length === 0
  ) {
    return false;
  }

  const menuIds = category.children.map((m) => String(m.id));

  // 确保字符串类型比较一致
  const selectedMenuIdsInCategory = menuIds.filter((id) =>
    selectedMenuIds.value.includes(id),
  ).length;

  if (selectedMenuIdsInCategory === 0) return false;
  if (selectedMenuIdsInCategory === menuIds.length) return true;
  return 'indeterminate';
}

// 暴露抽屉API给父组件
defineExpose({
  drawerApi,
});
</script>

<template>
  <Drawer class="w-[700px]">
    <div>
      <div class="mb-2">
        <div class="flex items-center">
          <span class="text-base font-medium">{{ currentRole?.name }}</span>
          <span class="ml-1 text-xs text-gray-500">
            ({{ currentRole?.code }})
          </span>
        </div>
        <p class="text-xs text-gray-500">{{ currentRole?.description }}</p>
      </div>

      <Spin :spinning="initialLoading">
        <div class="menu-container">
          <div class="mb-2 text-xs text-gray-500">
            {{ $t('system.role.menuTip') }}
          </div>

          <Empty
            v-if="categorizedMenus.length === 0"
            :description="$t('common.noData')"
          />

          <div v-else class="menu-category-list">
            <Card
              v-for="category in categorizedMenus"
              :key="category.id"
              class="mb-2"
              :bordered="true"
              :head-style="{ background: '#f6f8fa', padding: '6px 12px' }"
              :body-style="{ padding: '8px 12px' }"
            >
              <!-- 分类标题与全选控制 -->
              <template #title>
                <div class="flex items-center">
                  <Checkbox
                    :checked="getCategoryCheckStatus(category) === true"
                    :indeterminate="
                      getCategoryCheckStatus(category) === 'indeterminate'
                    "
                    @change="
                      (e) => toggleCategoryMenus(category, e.target.checked)
                    "
                  >
                    <span class="text-sm font-medium">{{ category.name }}</span>
                  </Checkbox>
                </div>
              </template>

              <!-- 分类下的菜单列表 -->
              <div
                v-if="category.children && category.children.length > 0"
                class="menu-list"
              >
                <div class="grid grid-cols-4 gap-2">
                  <div
                    v-for="menu in category.children"
                    :key="menu.id"
                    class="menu-item"
                  >
                    <Checkbox
                      :checked="selectedMenuIds.includes(String(menu.id))"
                      :disabled="menu.status === 0"
                      @change="
                        (e) =>
                          handleMenuChange(String(menu.id), e.target.checked)
                      "
                    >
                      <div class="menu-info">
                        <div class="menu-name text-xs">
                          {{ $t(menu.menuName) }}
                        </div>
                        <div class="menu-code text-xs text-gray-400">
                          {{ menu.perms }}
                        </div>
                      </div>
                    </Checkbox>
                  </div>
                </div>
              </div>
              <Empty
                v-else
                :description="$t('system.menu.noCategoryMenu')"
                :image-style="{ height: '40px' }"
              />
            </Card>
          </div>
        </div>
      </Spin>
    </div>
  </Drawer>
</template>

<style scoped>
.menu-container {
  overflow-y: auto;
}

.menu-category-list {
  margin-bottom: 0.5rem;
}

.menu-item {
  padding: 0;
  margin-bottom: 0.25rem;
  border-radius: 3px;
  transition: all 0.2s;
}

.menu-item:hover {
  background-color: #f5f7fa;
}

.menu-info {
  padding: 0;
  line-height: 1.2;
}

:deep(.ant-card-head) {
  min-height: auto;
}

:deep(.ant-card-body) {
  padding-top: 6px;
  padding-bottom: 6px;
}

:deep(.ant-checkbox-wrapper) {
  font-size: 12px;
}

:deep(.ant-empty) {
  margin: 8px 0;
}

:deep(.ant-empty-image) {
  height: 40px;
}

:deep(.ant-empty-description) {
  font-size: 12px;
}
</style>
